﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Linq.Expressions;
using Sitecore.Pipelines.RenderField;
using Sitecore.Pipelines;
using Sitecore.Xml.Xsl;
using Sitecore.Mvc.Extensions;
using System.Reflection;
using System.Collections.Concurrent;
using System.Web;
using ScapiContext = global::Sitecore.Context;
using ScapiItem = global::Sitecore.Data.Items.Item;
using ScapiRenderFieldArgs = global::Sitecore.Pipelines.RenderField.RenderFieldArgs;
using ScapiCorePipeline = global::Sitecore.Pipelines.CorePipeline;
using ScapiSafeDictionary = global::Sitecore.Collections.SafeDictionary<string>;
using System.Globalization;
using ScapiRenderFieldResult = global::Sitecore.Xml.Xsl.RenderFieldResult;

namespace Pocahontas.Sitecore.Mvc
{
	/// <summary>
	/// Represents support for Sitecore controls in an application.
	/// </summary>
	public static class SitecoreHtmlExtensions
	{
		/// <summary>
		/// Returns the correct markup for a Sitecore field for each property in the object that is represented by the specified expression.
		/// </summary>
		/// <typeparam name="TModel">The type of the model.</typeparam>
		/// <typeparam name="TProperty">The type of the value.</typeparam>
		/// <param name="htmlHelper">The HTML helper instance that this method extends.</param>
		/// <param name="expression">An expression that identifies the object that contains the properties to render.</param>
		/// <param name="htmlAttributes">An object that contains the HTML attributes to set for the element.</param>
		/// <returns>Markup for the Sitecore field.</returns>
		public static IHtmlString FieldFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes = null)
		{
			return FieldFor<TModel, TProperty>(htmlHelper, expression, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
		}

		/// <summary>
		/// Returns the correct markup for a Sitecore field for each property in the object that is represented by the specified expression.
		/// </summary>
		/// <typeparam name="TModel">The type of the model.</typeparam>
		/// <typeparam name="TProperty">The type of the value.</typeparam>
		/// <param name="htmlHelper">The HTML helper instance that this method extends.</param>
		/// <param name="expression">An expression that identifies the object that contains the properties to render.</param>
		/// <param name="htmlAttributes">A dictionary that contains the HTML attributes to set for the element.</param>
		/// <returns>Markup for the Sitecore field.</returns>
		public static IHtmlString FieldFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes)
		{
			return new HtmlString(RenderField(expression, htmlHelper.ViewData, htmlAttributes).ToString());
		}

		/// <summary>
		/// Returns the correct markup for a Sitecore image for each property in the object that is represented by the specified expression.
		/// </summary>
		/// <typeparam name="TModel">The type of the model.</typeparam>
		/// <typeparam name="TProperty">The type of the value.</typeparam>
		/// <param name="htmlHelper">The HTML helper instance that this method extends.</param>
		/// <param name="expression">An expression that identifies the object that contains the properties to render.</param>
		/// <param name="htmlAttributes">An object that contains the HTML attributes to set for the element.</param>
		/// <returns>Markup for the image.</returns>
		public static IHtmlString ImageFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes = null)
		{
			return ImageFor<TModel, TProperty>(htmlHelper, expression, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
		}

		/// <summary>
		/// Returns the correct markup for a Sitecore image for each property in the object that is represented by the specified expression.
		/// </summary>
		/// <typeparam name="TModel">The type of the model.</typeparam>
		/// <typeparam name="TProperty">The type of the value.</typeparam>
		/// <param name="htmlHelper">The HTML helper instance that this method extends.</param>
		/// <param name="expression">An expression that identifies the object that contains the properties to render.</param>
		/// <param name="htmlAttributes">A dictionary that contains the HTML attributes to set for the element.</param>
		/// <returns>Markup for the image.</returns>
		public static IHtmlString ImageFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes)
		{
			return FieldFor<TModel, TProperty>(htmlHelper, expression, SitecoreHtmlAttributeDictionary.FromImageAttributes(htmlAttributes));
		}

		public static SitecoreHtmlLinkString<TModel> LinkFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes)
		{
			return new SitecoreHtmlLinkString<TModel>(htmlHelper, (contents) =>
			{
				ScapiRenderFieldResult result = RenderField(expression, htmlHelper.ViewData, htmlAttributes);

				if (!String.IsNullOrEmpty(contents))
				{
					htmlAttributes["haschildren"] = "true";
				}

				return result.FirstPart + contents + result.LastPart;
			});
		}

		public static SitecoreHtmlLinkString<TModel> LinkFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes = null)
		{
			return LinkFor<TModel, TProperty>(htmlHelper, expression, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
		}

		internal static ScapiRenderFieldResult RenderField<TModel, TProperty>(Expression<Func<TModel, TProperty>> expression, ViewDataDictionary<TModel> viewData, IDictionary<string, object> htmlAttributes)
		{
			SitecoreHtmlMetadata metadata = SitecoreHtmlMetadata.FromLambdaExpression<TModel, TProperty>(expression, viewData);

			ScapiRenderFieldArgs args = new ScapiRenderFieldArgs
			{
				Item = SitecoreItemGateway.GetScapiItem(metadata.Key, true),
				FieldName = metadata.FieldName,
				Parameters = SitecoreHtmlAttributeDictionary.FromAttributes(htmlAttributes)
			};

			ScapiCorePipeline.Run("renderField", args);

			return args.Result;
		}
	}
}
